package org.open.otalk.api.r1;

import java.security.GeneralSecurityException;

import javax.net.ssl.SSLContext;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.provider.DelayInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.pubsub.provider.EventProvider;
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
import org.jivesoftware.smackx.pubsub.provider.PubSubProvider;
import org.open.otalk.api.r1.listener.PingListener;
import org.open.otalk.api.r1.packet.PingExtension;
import org.open.otalk.api.ssl.MemorizingTrustManager;





import android.content.Context;
import android.os.RemoteException;
import android.util.Log;

public class OTalkClient {
	private static final String TAG = "OTalkClient";
	//
	private static final int DEFAULT_XMPP_PORT = 5222;
	private  XMPPConnection connection;
	private  ConnectionConfiguration mConnectionConfiguration;
	private  OUserInfo userInfo;
	private  String mUser;
    private  String mPassword;
    private  String mResource;
    private  String mErrorMsg;
    private  String mHost;
    private  int    mPort = DEFAULT_XMPP_PORT;
	
    //-------Listener---------------
    private  PingListener mPingListener = new PingListener(this);
	//-------Listener-------------------
    //-------Manager--------------------
    private OTalkRoster      mRosterManager;
    private OTalkChatManager mChatManager;
    private OSearchManager   mSearchManager;
    private OVCardManager    mVcardManager;
    //-------Manager--------------------
	private Context mContext;
	public OTalkClient(Context context) {
		this.mContext = context;
		//
		
		Roster.setDefaultSubscriptionMode(SubscriptionMode.manual);
		configure(ProviderManager.getInstance());
	}
	public Context getContext()
	{
		return this.mContext;
	}
	private boolean settings_key_specific_server = false;
	private boolean settings_key_xmpp_tls_use = false;
	private boolean settings_key_gmail = false;
	private boolean SMACK_DEBUG_KEY = false;
	private boolean mUseProxy = false;
	private ConnectionConfiguration initConnection(String mHost,int mPort)
	{
		
//		if (mUseProxy) {
//		    String stype = mSettings.getString(BeemApplication.PROXY_TYPE_KEY, "HTTP");
//		    String phost = mSettings.getString(BeemApplication.PROXY_SERVER_KEY, "");
//		    String puser = mSettings.getString(BeemApplication.PROXY_USERNAME_KEY, "");
//		    String ppass = mSettings.getString(BeemApplication.PROXY_PASSWORD_KEY, "");
//		    ProxyInfo.ProxyType type = ProxyType.valueOf(stype);
//		    mProxyInfo = new ProxyInfo(type, phost, pport, puser, ppass);
//		} else {
//		    mProxyInfo = ProxyInfo.forNoProxy();
//		}
		
		 mConnectionConfiguration = new ConnectionConfiguration(mHost,mPort);
//		if (settings_key_specific_server)
//		    mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort, mService, mProxyInfo);
//		else{
//		    mConnectionConfiguration = new ConnectionConfiguration(mService, mProxyInfo);
//		}
		//
		if (settings_key_xmpp_tls_use||settings_key_gmail) {
		    mConnectionConfiguration.setSecurityMode(SecurityMode.required);
		}
		if (SMACK_DEBUG_KEY){
		    mConnectionConfiguration.setDebuggerEnabled(true);
		mConnectionConfiguration.setSendPresence(false);
		// maybe not the universal path, but it works on most devices (Samsung Galaxy, Google Nexus One)
		mConnectionConfiguration.setTruststoreType("BKS");
		mConnectionConfiguration.setTruststorePath("/system/etc/security/cacerts.bks");
		installMemorizingTrustManager(mConnectionConfiguration);
	    }
		
		return mConnectionConfiguration;
	}
	 /**
     * Install the MemorizingTrustManager in the ConnectionConfiguration of Smack.
     *
     * @param config the configuration to modify
     */
    private void installMemorizingTrustManager(ConnectionConfiguration config) {
	try {
	    SSLContext sc = SSLContext.getInstance("TLS");
	    sc.init(null, MemorizingTrustManager.getInstanceList(getContext()),
		    new java.security.SecureRandom());
	    config.setCustomSSLContext(sc);
	} catch (GeneralSecurityException e) {
	    Log.w(TAG, "Unable to use MemorizingTrustManager", e);
	}
    }

    /**
     * A sort of patch from this thread: http://www.igniterealtime.org/community/thread/31118. Avoid ClassCastException
     * by bypassing the classloading shit of Smack.
     * @param pm The ProviderManager.
     */
    private void configure(ProviderManager pm) {
	Log.d(TAG, "configure");
	// Service Discovery # Items
	pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
	// Service Discovery # Info
	pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());

	// Privacy
	//pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
	// Delayed Delivery only the new version
	pm.addExtensionProvider("delay", "urn:xmpp:delay", new DelayInfoProvider());

	// Service Discovery # Items
	pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
	// Service Discovery # Info
	pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());

	// Chat State
	ChatStateExtension.Provider chatState = new ChatStateExtension.Provider();
	pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", chatState);
	pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates",
	    chatState);
	pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", chatState);
	pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", chatState);
	pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", chatState);
	// capabilities
//	pm.addExtensionProvider("c", "http://jabber.org/protocol/caps", new CapsProvider());
	//Pubsub
	pm.addIQProvider("pubsub", "http://jabber.org/protocol/pubsub", new PubSubProvider());
	pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub", new ItemsProvider());
	pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub", new ItemsProvider());
	pm.addExtensionProvider("item", "http://jabber.org/protocol/pubsub", new ItemProvider());

	pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub#event", new ItemsProvider());
	pm.addExtensionProvider("item", "http://jabber.org/protocol/pubsub#event", new ItemProvider());
	pm.addExtensionProvider("event", "http://jabber.org/protocol/pubsub#event", new EventProvider());
	//TODO rajouter les manquants pour du full pubsub


	//PEP avatar
//	pm.addExtensionProvider("metadata", "urn:xmpp:avatar:metadata", new AvatarMetadataProvider());
//	pm.addExtensionProvider("data", "urn:xmpp:avatar:data", new AvatarProvider());

//         PEPProvider pep  = new PEPProvider();
//         AvatarMetadataProvider avaMeta  = new AvatarMetadataProvider();
//         pep.registerPEPParserExtension("urn:xmpp:avatar:metadata", avaMeta);
//         pm.addExtensionProvider("event", "http://jabber.org/protocol/pubsub#event", pep);

	// ping
	pm.addIQProvider(PingExtension.ELEMENT, PingExtension.NAMESPACE, PingExtension.class);

	/*
	// Private Data Storage
	pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
	// Time
	try {
	    pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
	} catch (ClassNotFoundException e) {
	    Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Time");
	}
	// Roster Exchange
	pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
	// Message Events
	pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
	// XHTML
	pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
	// Group Chat Invitations
	pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider());
	// Data Forms
	pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
	// MUC User
	pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
	// MUC Admin
	pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
	// MUC Owner
	pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
	// Version
	try {
	    pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
	} catch (ClassNotFoundException e) {
	    // Not sure what's happening here.
	    Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Version");
	}
	// VCard
	pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
	// Offline Message Requests
	pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
	// Offline Message Indicator
	pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
	// Last Activity
	pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
	// User Search
	pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
	// SharedGroupsInfo
	pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup",
	    new SharedGroupsInfo.Provider());
	// JEP-33: Extended Stanza Addressing
	pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider());
	// FileTransfer
	pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
	pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
	pm.addIQProvider("open", "http://jabber.org/protocol/ibb", new IBBProviders.Open());
	pm.addIQProvider("close", "http://jabber.org/protocol/ibb", new IBBProviders.Close());
	pm.addExtensionProvider("data", "http://jabber.org/protocol/ibb", new IBBProviders.Data());

	pm.addIQProvider("command", COMMAND_NAMESPACE, new AdHocCommandDataProvider());
	pm.addExtensionProvider("malformed-action", COMMAND_NAMESPACE,
	    new AdHocCommandDataProvider.MalformedActionError());
	pm.addExtensionProvider("bad-locale", COMMAND_NAMESPACE,
	    new AdHocCommandDataProvider.BadLocaleError());
	pm.addExtensionProvider("bad-payload", COMMAND_NAMESPACE,
	    new AdHocCommandDataProvider.BadPayloadError());
	pm.addExtensionProvider("bad-sessionid", COMMAND_NAMESPACE,
	    new AdHocCommandDataProvider.BadSessionIDError());
	pm.addExtensionProvider("session-expired", COMMAND_NAMESPACE,
	    new AdHocCommandDataProvider.SessionExpiredError());
	 */
    }
	public XMPPConnection getTalkConnection()
	{
		return this.connection;
	}
	public void sendPacket(Packet packet)
	{
		if(connection!=null){
			connection.sendPacket(packet);
		}else {
			
		}
	}
	
	public void sendPacketFile()
	{
		
	}
	
	private void initFeatures() {
		
	}
	
	private void initPEP() {

	}

	private void discoverServerFeatures() {

	}
	
	public void addConnectionListener() {

	}

	public void removeConnectionListener() {
	}

	protected boolean connect() {
		if(connection!=null && connection.isConnected())
			return true;
		else{
			try{
				ConnectionConfiguration config = initConnection(mHost, mPort);
				connection = new XMPPConnection(config);
				connection.connect();
				return true;
			}catch(Exception ex){
				ex.printStackTrace();
			}
		}
		 return false;
	}
	private boolean login(String _user, String _password) {
		if(connection==null || !connection.isAuthenticated())
			return false;
		if(connection.isAuthenticated())
			return true;
		try{
			initFeatures();
			//
			PacketFilter filter = new PacketTypeFilter(PingExtension.class);
			connection.addPacketListener(mPingListener, filter);
			connection.login(mUser, mPassword, mResource);
			userInfo = new OUserInfo();
			//
			discoverServerFeatures();
			mRosterManager = new OTalkRoster(this);
			mChatManager   = new OTalkChatManager(this);
			mSearchManager = new OSearchManager(this);
			mVcardManager  = new OVCardManager(this);
			return true;
		}catch(Exception ex){
			ex.printStackTrace();
			return false;
		}
	}
	private boolean loginAsync(final String _user,final String _password) {
		return false;
	}
	public boolean connectLogin(final String _user,final String _password)
	{
		if(connect())
			return login(_user,_password);
		return false;
	}

	public void connectLoginsync(final String _user, final String _password) {
		if (connection == null)
			return;

		Thread t = new Thread(new Runnable() {

			@Override
			public void run() {
				loginAsync(_user, _password);
			}
		});
		t.start();
	}
	
	public void changeStatus() {

	}
	public boolean logOut()
	{
		return false;
	}
	public boolean isAuthentificated() {
		return connection.isAuthenticated();
	    }
	public boolean disconnect() {
		return false;
	}

}
